#ifndef _FENGINE_CORE_ENGINE
#define _FENGINE_CORE_ENGINE

#include <iostream>
#include <ctime>
#include <windowsx.h>
#include "FEApplication.h"
#include "FEngineException.h"
#include "Camera.h"
#include "InputManager.h"
#include "ColorRGB.h"
#include "SkyDome.h"


namespace FEngine
{
	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

	class Camera;
	class SkyDome;

	/**
	 * Base class of the FEngine, initializing the window and running the main loop.
	 */
	class CoreEngine
	{
		public:
			
			/**
			 * Initialize the engine and DirectX.
			 * @param _hInstance Instance of the program supplied by the WinMain function.
			 * @param _screenWidth Size of the window width.
			 * @param _screenHeight Size of the window height.
			 * @param _backgroundColor Color of the background of the scene ((R, G, B) value between 0 and 255).
			 * @return True if the initialization is done.
			 */
			bool initialize(HINSTANCE _hInstance, unsigned short _screenWidth, unsigned short _screenHeight, const ColorRGB& _backgroundColor = ColorRGB(0, 0, 0));
			
			/**
			 * Register the main class of the application.
			 * @param _app Main class of the program.
			 */
			void setApplication(FEApplication* _app);
			
			/**
			 * Return the DeviceInterface of the program.
			 * @return The interface with DirectX9.
			 */
			IDirect3DDevice9* getDeviceInterface() const;
			
			/**
			 * Return the ShaderEffect of the program.
			 * @return The main shader of the engine.
			 */
			LPD3DXEFFECT getShaderEffect() const;

			/**
			 * Stop the CoreEngine.
			 */
			void stop();


			/**
			 * Define the active camera.
			 * @param _cam Camera to use to render the scene.
			 */
			void setCamera(Camera* _cam);
			
			/**
			 * Get the active camera.
			 * @return Camera uses to render the scene.
			 */
			Camera* getCamera() const;
			


			/**
			 * Set the background color of the scene.
			 * @param _color Color of the background.
			 */
			void setBackgroundColor(const ColorRGB& _color);
			
			/**
			 * Set the background color of the scene.
			 * @param _red Red componant of the color of the background.
			 * @param _green Green componant of the color of the background.
			 * @param _blue Blue componant of the color of the background.
			 */
			void setBackgroundColor(unsigned char _red, unsigned char _green, unsigned char _blue);
			
			/**
			 * Get the background color of the scene.
			 * @return Color of the background.
			 */
			ColorRGB getBackgroundColor() const;
			

			
			/**
			 * Create a skydome for the scene.
			 * @param _fileName Name of the cube map texture file to use.
			 * @param _rotationSpeed Speed of the rotation in deegree per seconds.
			 */
			void setSkyDome(const std::string& _fileName, float _rotationSpeed = 0);
			
			/**
			 * Disabled the sky dome of the scene.
			 */
			void disableSkyDome();
			
			/**
			 * Enabled the sky dome of the scene if it has been disabled.
			 * You dont need to call this method after a call to 'setSkyDome()'
			 */
			void enableSkyDome();
			
			/**
			 * Set the rotation speed of the sky dome.
			 * @param _speed Speed of the rotation in deegree per seconds.
			 */
			void setSkyDomeRotateSpeed(float _speed);



			/**
			 * Get the window width.
			 * @return Width of the window.
			 */
			int getScreenWidth() const;
			
			/**
			 * Get the window height.
			 * @return Height of the window.
			 */
			int getScreenHeight() const;


			
			/**
			 * Set the color of the fog.
			 * @param _color Color of the background.
			 */
			void setFogColor(const ColorRGB& _color);
			
			/**
			 * Set the color of the fog.
			 * @param _red Red componant of the color of the fog.
			 * @param _green Green componant of the color of the fog.
			 * @param _blue Blue componant of the color of the fog.
			 */
			void setFogColor(unsigned char _red, unsigned char _green, unsigned char _blue);
			
			/**
			 * Set the fog start distance.
			 * @param _dist Start distance of the fog.
			 */
			void setFogStartDistance(float _dist);
			
			/**
			 * Set the fog end distance.
			 * @param _dist End distance of the fog.
			 */
			void setFogEndDistance(float _dist);
			
			/**
			 * Enable the fog.
			 */
			void enableFog();
			
			/**
			 * Disable the fog.
			 */
			void disableFog();



			/**
			* Release the allocated memory.
			*/
			void cleanUp();

			/**
			 * Main loop of the program.
			 * Call it once when the initialization stuff is done.
			 */
			void run();


		private:
			
			HWND				mWnd;				// Windows handle used in DirectX initialization
			LPDIRECT3D9			mD3D;				// Pointer to the Direct3D interface
			IDirect3DDevice9*	mDeviceInterface;	// Pointer to the device interface
			LPD3DXEFFECT		mShaderEffect;

			bool				mIsInitialized;
			bool				mIsRunning;			// True while we want the program to run
			unsigned short		mScreenWidth;
			unsigned short		mScreenHeight;
			ColorRGB			mBackgroundColor;
			
			FEApplication*		mApplication;
			FEApplication*		mNextApplication;	// If the application has changed, register the new one to make the change at the end of the current frame
			Camera*				mCamera;

			SkyDome*			mSkyDome;

			bool				mFogEnabled;
			ColorRGB			mFogColor;
			float				mFogStartDistance;
			float				mFogEndDistance;

			bool _initWindow(HINSTANCE hInstance);
			bool _initD3Ddevice();


#pragma region Singleton

		private:

			static CoreEngine* mInstance;
			CoreEngine();
			~CoreEngine();

		public:
			
			static CoreEngine* getInstance()
			{
				if (mInstance == NULL)
					mInstance = new CoreEngine();
				return mInstance;
			}

			static void deleteInstance()
			{
				if (mInstance == NULL)
					return;

				delete mInstance;
				mInstance = NULL;
			}

			CoreEngine(CoreEngine const&);
			void operator=(CoreEngine const&);

#pragma endregion

	};

} // namespace FEngine

#endif
